#include<string>
#include<iostream>
#include<fstream>
#include<cstdio>
#include<cstring>
#include "romfile.h"
using namespace std;

romfile::romfile(string filename) {
    cout<<"romfile opening "<<filename<<"."<<endl;
    valid=load(filename);
    mapper=0;
    ram=false;
    batt=false;
    rumble=false;
    sram=false;
    timer=false;
    if(valid) {
        cout<<"Loaded the file."<<endl;
        fill_info();
        print_info();
    }
    else {
        cout<<"Couldn't load the file. Check path, and see if it's a real GameBoy rom."<<endl;
    }
}

bool romfile::isvalid() {
    return valid;
}

bool romfile::load(string filename) {
    std::ifstream filein;
    filein.open(filename.c_str(),std::ios::binary|std::ios::in);
    if(!(filein&&filein.is_open())) {
        cout<<"Couldn't open file."<<endl;
        return false;
    }
    filein.seekg(0,std::ios::end);
    filesize=filein.tellg();
    filein.seekg(0,std::ios::beg);
    unsigned char check[]={0xce,0xed,0x66,0x66,0xcc,0x0d,0x00,0x0b,0x03,0x73,0x00,0x83,0x00,0x0c,0x00,0x0d,
                   0x00,0x08,0x11,0x1f,0x88,0x89,0x00,0x0e,0xdc,0xcc,0x6e,0xe6,0xdd,0xdd,0xd9,0x99,
                   0xbb,0xbb,0x67,0x63,0x6e,0x0e,0xec,0xcc,0xdd,0xdc,0x99,0x9f,0xbb,0xb9,0x33,0x3e};
    //filein.seekg(0x104,std::ios::beg);
    contents=new unsigned char[filesize];
    filein.read(const_cast<char *>(reinterpret_cast<const char *>(contents)),filesize);
    int check_index=0;
    for(int i=0x0;i<filesize;i++) {
        if(check_index==47&&contents[i]==check[check_index]) {
            printf("Found correct header starting at %04x and ending at %04x\n",i-47,i);
            return true;
        }
        else if(contents[i]!=check[check_index]) {
            //printf("Expected byte %02x, found byte %02x at file position %04x\n",check[check_index],contents[i],i);
            check_index=0;
        }
        else {
            check_index++;
        }
    }
    cout<<"This doesn't look like a GameBoy ROM."<<endl;
    return false;   
}

void romfile::fill_info() {
    if(valid) {
        title=new char[16];
        strncpy(title,reinterpret_cast<const char *>(&(contents[0x134])),16);
        color=((unsigned char)contents[0x143]==0x80)?true:false;
        sgb_support=((unsigned char)contents[0x146]==3)?true:false;
        cart_type=(unsigned char)contents[0x147];
        //mapper,ram,batt,rumble,sram,timer
        switch(cart_type) {
        case 0x0:
                break;
        case 0x1:
                mapper=1;
                break;
        case 0x2:
                mapper=1;
                ram=true;
                break;
        case 0x3:
                mapper=1;
                ram=true;
                batt=true;
                break;
        case 0x5:
                mapper=2;
                break;
        case 0x6:
                mapper=2;
                batt=true;
                break;
        case 0x8:
                ram=true;
                break;
        case 0x9:
                ram=true;
                batt=true;
                break;
        case 0xb:
                mapper=3;
                break;
        case 0xc:
                mapper=3;
                sram=true;
                break;
        case 0xd:
                mapper=3;
                sram=true;
                batt=true;
                break;
        case 0xf:
                mapper=4;
                timer=true;
                batt=true;
                break;
        case 0x10:
                mapper=4;
                timer=true;
                ram=true;
                batt=true;
                break;
        case 0x11:
                mapper=4;
                break;
        case 0x12:
                mapper=4;
                ram=true;
                break;
        case 0x13:
                mapper=4;
                ram=true;
                batt=true;
                break;
        case 0x19:
                mapper=5;
                break;
        case 0x1a:
                mapper=5;
                ram=true;
                break;
        case 0x1b:
                mapper=5;
                ram=true;
                batt=true;
                break;
        case 0x1c:
                mapper=5;
                rumble=true;
                break;
        case 0x1d:
                mapper=5;
                rumble=true;
                sram=true;
                break;
        case 0x1e:
                mapper=5;
                rumble=true;
                sram=true;
                batt=true;
                break;
        case 0x1f:
                mapper=6;
                break;
        case 0xfd:
                mapper=7;
                break;
        case 0xfe:
                mapper=8;
                break;
        case 0xff:
                mapper=9;
                break;
        default: cout<<"I don't recognize this mapper!"<<endl;
        }
        switch(contents[0x148]) {//multiples of 16KB banks
        case 0x0: rom_banks=2; break;
        case 0x1: rom_banks=4; break;
        case 0x2: rom_banks=8; break;
        case 0x3: rom_banks=16; break;
        case 0x4: rom_banks=32; break;
        case 0x5: rom_banks=64; break;
        case 0x6: rom_banks=128; break;
        case 0x52: rom_banks=72; break;
        case 0x53: rom_banks=80; break;
        case 0x54: rom_banks=96; break;
        default: cout<<"I don't know how many banks of ROM this has =("<<endl;
        }
        switch(contents[0x149]) {//multiples of 2KB banks (1 bank is actually 8KB)
        case 0: ram_banks=0; break;
        case 1: ram_banks=1; break;
        case 2: ram_banks=4; break;
        case 3: ram_banks=16; break;
        case 4: ram_banks=64; break;
        default: cout<<"I don't know how much RAM this has =( (it says "<<((int)(contents[0x45]))<<")."<<endl;
        }
    }
    else {
        cout<<"Not a valid rom file."<<endl;
    }
}

void romfile::print_info() {
    string mapper_name;
    switch(mapper) {
    case 0x0: mapper_name="none"; break;
    case 0x1: mapper_name="MBC1"; break;
    case 0x2: mapper_name="MBC2"; break;
    case 0x3: mapper_name="MMM01"; break;
    case 0x4: mapper_name="MBC3"; break;
    case 0x5: mapper_name="MBC5"; break;
    case 0x6: mapper_name="Pocket Camera"; break;
    case 0x7: mapper_name="Bandai TAMA5"; break;
    case 0x8: mapper_name="Hudson HuC-3"; break;
    case 0x9: mapper_name="Hudson HuC-1"; break;
    default: mapper_name="unknown";
    }
    printf("Filesize: %d\nTitle: %s\nmapper: %s\nram: %s\nbattery: %s\nrumble: %s\nsram: %s\ntimer: %s\nColor: %s\nSuper GameBoy support: %s\nROM size: %dKB\nRAM size: %dKB\n",filesize,title,mapper_name.c_str(),ram?"present":"absent",batt?"present":"absent",rumble?"present":"absent",sram?"present":"absent",timer?"present":"absent",color?"yes":"no",sgb_support?"yes":"no",rom_banks*16,ram_banks*2);
}
